﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;

namespace eWallet.Security.Cryptography
{
	public sealed class PasswordCryptoModule : CryptoModule
	{

		public PasswordCryptoModule(string password)
		{
			if (string.IsNullOrEmpty(password))
				throw new ArgumentNullException("password");

			this.Password = password;
		}

		private string Password;

		public override EncryptionMethod EncryptionMethod
		{
			get { return EncryptionMethod.Password; }
		}



		public override byte[] Decrypt(byte[] cipher)
		{
			return this.CreateAlgorithm(this.Password).CreateDecryptor().TransformFinalBlock(cipher, 0, cipher.Length);
		}

		public override byte[] Encrypt(byte[] data)
		{
			return this.CreateAlgorithm(this.Password).CreateEncryptor().TransformFinalBlock(data, 0, data.Length);
		}

		public override string GetDescription()
		{
			byte[] hash = SHA1CryptoServiceProvider.Create().ComputeHash(Encoding.UTF8.GetBytes(this.Password));
			return string.Format("{0} SHA1 {1}", this.EncryptionMethod, BitConverter.ToString(hash));
		}
		


		private SymmetricAlgorithm CreateAlgorithm(string password)
		{
			if (string.IsNullOrEmpty(password))
				throw new ArgumentNullException("password");

			var alg = SymmetricAlgorithm.Create("Rijndael");

			// The max keysize for Rijndael is 256 bit, which eqals 32 bytes.
			// The IV for the Rijndael algorithm is 16 byte, so we need to produce
			// 48 bytes of data generated out of the password.
			// We do this by first encoding the password using the UTF8 encoding.
			// Then we create a hash out of that encoded password by using the
			// SHA 384 hash algorithm. This algorithm prodces exactly 48 bytes
			// of data. We use the first 32 bytes of that data as the key
			// and the remaining 16 as the IV for the Rijndael symmetric algorithm.
			// This way we can produce an identical algorithm if the password
			// is also identical.

			byte[] pwdBuffer = Encoding.UTF8.GetBytes(password);
			var hash = SHA384Managed.Create().ComputeHash(pwdBuffer);

			byte[] key = new byte[32];
			Array.Copy(hash, 0, key, 0, 32);
			alg.Key = key;

			byte[] iv = new byte[16];
			Array.Copy(hash, 32, iv, 0, 16);
			alg.IV = iv;

			return alg;
		}

	}
}
